package com.hwtx.form.domain.handle;

import cn.hutool.extra.spring.SpringUtil;
import com.google.common.collect.Maps;
import com.hwtx.form.domain.FormConstants;
import com.hwtx.form.domain.def.meta.config.FormComponentConfig;
import com.hwtx.form.domain.service.CommonApiInvoker;
import com.hwtx.form.persistence.entity.ApiDef;
import com.hwtx.form.util.FormUtil;
import io.geekidea.boot.framework.exception.BusinessException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

public class ColumnCallbackMapper implements RowMapper<Map<String, Object>> {
    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private final ColumnCallback callback;
    private final List<FormComponentConfig.ApiInvokeParam> invokeApiFields;

    public ColumnCallbackMapper(ColumnCallback callback, List<FormComponentConfig.ApiInvokeParam> invokeApiFields) {
        this.callback = callback;
        this.invokeApiFields = invokeApiFields;
    }

    // 内置的日期格式处理器
    public static ColumnCallback RowInterceptor() {
        return (columnName, value) -> {
            // 自动处理 LocalDateTime
            if (value instanceof LocalDateTime) {
                return ((LocalDateTime) value).format(formatter);
            }
            // 自动处理 Timestamp
            if (value instanceof java.sql.Timestamp) {
                return ((java.sql.Timestamp) value).toLocalDateTime().format(formatter);
            }
            return value; // 非日期类型保持原样
        };
    }

    @Override
    public Map<String, Object> mapRow(ResultSet rs, int rowNum) throws SQLException {
        Map<String, Object> rowMap = new LinkedHashMap<>();
        ResultSetMetaData metaData = rs.getMetaData();
        int columnCount = metaData.getColumnCount();

        for (int i = 1; i <= columnCount; i++) {
            String columnName = metaData.getColumnLabel(i);
            Object value = rs.getObject(i);

            // 应用回调处理
            rowMap.put(columnName, callback.processColumn(columnName, value));
        }
        if (CollectionUtils.isNotEmpty(invokeApiFields)) {
            CommonApiInvoker commonApiInvoker = SpringUtil.getBean(CommonApiInvoker.class);
            invokeApiFields.stream().filter(apiInvokeParam -> !Objects.isNull(apiInvokeParam.getComponentParamInfo())).forEach(apiInvokeParam -> {
                final Map<String, Object> input = new LinkedHashMap<>();
                FormComponentConfig formComponentConfig = apiInvokeParam.getFormComponentConfig();
                if (formComponentConfig == null) {
                    throw new BusinessException("组件配置错误，组件类型为" + apiInvokeParam.getComponentType().name() + "，但未配置组件配置");
                }
                ApiDef apiDef = FormUtil.getApiDef(apiInvokeParam.getComponentParamInfo().getCode());
                if (Objects.equals(apiInvokeParam.getComponentType(), FormConstants.FormComponentType.FORM_COM_CHAINED_SELECT)) {
                    if (MapUtils.isEmpty(apiInvokeParam.getFields())) {
                        throw new BusinessException("组件配置错误，组件类型为" + FormConstants.FormComponentType.FORM_COM_CHAINED_SELECT + "，但未配置字段");
                    }
                    List<String> inputParams = CommonApiInvoker.getInputParamNames(apiDef);
                    if (CollectionUtils.isEmpty(inputParams) || inputParams.size() > 1) {
                        throw new BusinessException("组件配置错误，组件类型为" + FormConstants.FormComponentType.FORM_COM_CHAINED_SELECT + "，输入参数数量不等于1");
                    }
                    if (BooleanUtils.toBoolean(apiDef.getBatch())) {
                        input.put(inputParams.getFirst(), apiInvokeParam.getFields().entrySet().stream().map(entry -> {
                            String field = entry.getKey();
                            Set<String> aliasSet = entry.getValue();
                            if (CollectionUtils.isEmpty(aliasSet)) {
                                return rowMap.get(field);
                            }
                            for (String alias : aliasSet) {
                                if (rowMap.containsKey(alias)) {
                                    return rowMap.get(alias);
                                }
                            }
                            return null;
                        }).filter(Objects::nonNull).toList());
                        CommonApiInvoker.InvokeData invokeData = commonApiInvoker.invokeApiNoCareTest(apiDef, input);
                        StringBuilder ret = new StringBuilder();
                        outHandle(invokeData, apiInvokeParam.getComponentParamInfo(), (name, value) -> ret.append(value).append("-"));
                        ret.deleteCharAt(ret.length() - 1);
                        rowMap.put(apiInvokeParam.getColumn(), ret.toString());
                    } else {
                        StringBuilder ret = new StringBuilder();
                        apiInvokeParam.getFields().forEach((field, aliasSet) -> {
                            String name = field;
                            if (CollectionUtils.isNotEmpty(aliasSet)) {
                                for (String alias : aliasSet) {
                                    if (rowMap.containsKey(alias)) {
                                        name = alias;
                                        break;
                                    }
                                }
                            }
                            input.put(inputParams.getFirst(), rowMap.get(name));
                            CommonApiInvoker.InvokeData invokeData = commonApiInvoker.invokeApiNoCareTest(apiDef, input);
                            outHandle(invokeData, apiInvokeParam.getComponentParamInfo(), (n, value) -> ret.append(value).append("-"));
                        });
                        ret.deleteCharAt(ret.length() - 1);
                        rowMap.put(apiInvokeParam.getColumn(), ret.toString());
                    }
                } else {
                    Map<String, Object> ret = Maps.newHashMap();
                    input.putAll(input(apiInvokeParam.getComponentParamInfo(), rowMap));
                    CommonApiInvoker.InvokeData invokeData = commonApiInvoker.invokeApiNoCareTest(apiDef, input);
                    outHandle(invokeData, apiInvokeParam.getComponentParamInfo(), ret::put);
                    rowMap.putAll(ret);
                }
            });
        }
        return rowMap;
    }

    private Map<String, Object> input(FormComponentConfig.ComponentParamInfo item, Map<String, Object> rowMap) {
        Map<String, Object> input = new LinkedHashMap<>();
        item.getInput().forEach(param -> {
            String name = param.getAlias();
            if (StringUtils.isBlank(name)) {
                name = param.getKey();
            }
            input.put(param.getKey(), rowMap.get(name));
        });
        return input;
    }

    private void outHandle(CommonApiInvoker.InvokeData invokeData, FormComponentConfig.ComponentParamInfo item, OutValueCallback outValueCallback) {
        if (invokeData != null) {
            if (CollectionUtils.isNotEmpty(invokeData.getRet())) {
                invokeData.getRet().forEach(row -> setValue(item, row, outValueCallback));
            } else {
                Object object = invokeData.getRetData();
                if (object instanceof Map) {
                    setValue(item, (Map<String, Object>) object, outValueCallback);
                } else {
                    item.getOutput().forEach(output -> {
                        String name = output.getAlias();
                        if (StringUtils.isBlank(name)) {
                            name = output.getKey();
                        }
                        if (outValueCallback != null) {
                            outValueCallback.outValue(name, object);
                        }
                    });
                }
            }
        }
    }

    private void setValue(FormComponentConfig.ComponentParamInfo item, Map<String, Object> row, OutValueCallback outValueCallback) {
        item.getOutput().forEach(output -> {
            Object value = row.get(output.getKey());
            String name = output.getAlias();
            if (StringUtils.isBlank(name)) {
                name = output.getKey();
            }
            if (outValueCallback != null) {
                outValueCallback.outValue(name, value);
            }
        });
    }

    interface OutValueCallback {
        void outValue(String name, Object value);
    }

    // 自定义列处理回调接口
    public interface ColumnCallback {
        Object processColumn(String columnName, Object value);
    }
}